﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;

namespace WindowsFormsApplication1
{
    class Message
    {
        public IPAddress Address;
        public int Port;
        public byte[] Bytes;
    }
    class BroadcastClient
    {
        // Manage state of broadcasting client
        const int udpRangeStart = 15123;
        const int localMaximumPortCount = 16;
        UdpClient udpClient;
        IPEndPoint udpReceiveEndPoint;
        List<IPEndPoint> udpSendEndPoints;

        public int LocalPort;
        public bool IsListening = false;
        public Queue<Message> MessagesReceived;

        // Set up prerequisites for sending and receiving messages
        public BroadcastClient()
        {
            MessagesReceived = new Queue<Message>();
            BeginListening();
            SetupSendPorts();
        }

        // Iterate through a range of ports, select first available and listen on it
        private void BeginListening()
        {
            var portTestCount = 0;
            var udpPortFound = false;
            do
            {
                try
                {
                    LocalPort = udpRangeStart + portTestCount;
                    udpReceiveEndPoint = new IPEndPoint(IPAddress.Any, LocalPort);
                    udpClient = new UdpClient(udpReceiveEndPoint);
                    udpPortFound = true;
                }
                catch (SocketException)
                {
                    portTestCount++;
                }
            } while (!udpPortFound && portTestCount < localMaximumPortCount);

            if (udpPortFound)
            {
                udpClient.BeginReceive(UdpMessageReceived, udpClient);
                IsListening = true;
            }
        }

        // Method to be called upon receiving any broadcast messages
        private void UdpMessageReceived(IAsyncResult asyncResult)
        {
            var receivedBytes = udpClient.EndReceive(asyncResult, ref udpReceiveEndPoint);
            udpClient.BeginReceive(UdpMessageReceived, udpClient);
            if (udpReceiveEndPoint.Port != LocalPort)
            {
                MessagesReceived.Enqueue(
                    new Message()
                    {
                        Address = udpReceiveEndPoint.Address,
                        Port = udpReceiveEndPoint.Port,
                        Bytes = receivedBytes
                    });
            }
        }

        private void SetupSendPorts()
        {
            udpSendEndPoints = new List<IPEndPoint>();
            for (var sendPortOffset = 0; sendPortOffset < localMaximumPortCount; sendPortOffset++)
            {
                udpSendEndPoints.Add(new IPEndPoint(IPAddress.Broadcast, udpRangeStart + sendPortOffset));
            }
        }

        public void Send(byte[] data)
        {
            foreach (var endpoint in udpSendEndPoints)
            {
                udpClient.BeginSend(data, data.Length, endpoint, UdpMessageSent, udpClient);
            }
        }

        private void UdpMessageSent(IAsyncResult asyncResult)
        {
        }
    }
}
